/*
 * © 2024 huayunliufeng保留所有权利, 依据MIT许可证发布。
 * 请勿更改或删除版权声明或此文件头。
 * 此代码是免费软件, 您可以重新分发和/或修改它。
 * 开源是希望它有用, 但不对代码做任何保证。
 * 如有疑问请联系: huayunliufeng@163.com
 */

package io.github.huayunliufeng.common.utils;

import io.github.huayunliufeng.common.annotation.CallClassType;
import io.github.huayunliufeng.common.annotation.HfValue;
import io.github.huayunliufeng.common.annotation.InjectIgnore;
import io.github.huayunliufeng.common.annotation.ValueFromMethod;
import lombok.extern.slf4j.Slf4j;
import io.github.huayunliufeng.common.intface.OneReturnFunction;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.ResultSet;
import java.util.*;

/**
 * 给类的属性自动注入值的相关方法。
 *
 * @author zhongq
 * @datetime 2024/3/22 9:29
 */
@Slf4j
final public class HylfDataUtil {

    @SuppressWarnings("all")
    public static <T> T mapToJavaBean(final Object object, final Class<T> beanType) {
        if (object instanceof Map) {
            Map map = (Map) object;
            return (T) mapToJavaBean(map, beanType);
        }
        return null;
    }

    /**
     * 将map转换为JavaBean, map中的key值必须和字段名称相同。
     *
     * @param map      存放数据的map
     * @param beanType JavaBean类型
     * @return JavaBean
     */
    public static <T> T mapToJavaBean(final Map<String, Object> map, final Class<T> beanType) {
        return mapToJavaBean(map::get, beanType);
    }

    public static <T, R> T mapToJavaBean(final OneReturnFunction<String, R> function, final Class<T> beanType) {
        String logFormat = "转换为JavaBean失败。[function = {}, beanType = {}]";
        return HylfFunUtil.methodVoidReturnExec(() -> {
            T newBean = beanType.getConstructor().newInstance();
            Class<?> tmpType = beanType;
            // 如果有父类, 一直往上找
            while (tmpType != null) {
                Field[] fields = tmpType.getDeclaredFields();
                for (Field field : fields) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        continue;
                    }
                    field.setAccessible(true);
                    HfValue hfValue = field.getAnnotation(HfValue.class);
                    String name = hfValue == null ? field.getName() : hfValue.value();
                    // 字段名要和prop的key匹配才能正确注入值
                    R value = function.apply(name);
                    Class<?> type = field.getType();
                    if (type.isInstance(value)) {
                        field.set(newBean, value);
                    } else {
                        field.set(newBean, mapToJavaBean(value, type));
                    }
                }
                // 获取其父类
                tmpType = tmpType.getSuperclass();
            }
            return newBean;
        }, logFormat, function, beanType);
    }

    /**
     * 从数据源中生成指定的JavaBean, 例如dataSrc有getName, getAge方法, beanType有name, age属性, 则可以通过该方法自动创建新的bean并调用相应的get方法赋值
     *
     * @param dataSrc  数据源
     * @param beanType JavaBean类型
     * @param <T>      T
     * @return 具有指定值的JavaBean
     */
    public static <T> T methodToJavaBean(final Object dataSrc, final Class<T> beanType) {
        String logFormat = "从数据源生成JavaBean失败。[dataSrc = {}, beanType = {}]";
        return HylfFunUtil.methodVoidReturnExec(() -> {
            if (dataSrc == null || beanType == null) {
                return null;
            }
            T newBean = beanType.getConstructor().newInstance();
            Class<?> tmpType = beanType;
            Class<?> dataSrcClass = dataSrc.getClass();
            while (tmpType != null) {
                Field[] fields = tmpType.getDeclaredFields();
                for (Field field : fields) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        continue;
                    }
                    field.setAccessible(true);
                    // 该字段是不是自定义的对象类型
                    CallClassType callClassType = field.getAnnotation(CallClassType.class);
                    // 如果是就需要递归调用, 找该对象类型的字段
                    if (callClassType != null) {
                        field.set(newBean, methodToJavaBean(dataSrc, field.getType()));
                    } else {
                        // 获取自定义的内容
                        ValueFromMethod valueFromMethod = field.getAnnotation(ValueFromMethod.class);
                        // 获取方法名称
                        String methodName = getMethodName(valueFromMethod, field.getName());
                        HylfFunUtil.methodVoidExec(() -> {
                            // 获取方法的入参, 暂时只支持八种基本类型和String类型的入参
                            Class<?>[] methodParamTypes = getMethodParamTypes(valueFromMethod);
                            // 无入参的情况下默认返回对象
                            if (HylfCollectionUtil.isEmpty(methodParamTypes)) {
                                Method method = dataSrcClass.getMethod(methodName);
                                field.set(newBean, method.invoke(dataSrc));
                            } else {
                                Method method = dataSrcClass.getMethod(methodName, methodParamTypes);
                                // 需要获取参数列表, 可以有多组入参, 因此是二维数组
                                Object[][] methodParamValues = getMethodParamValues(methodParamTypes, valueFromMethod);
                                int n = methodParamValues.length;
                                // 每组入参对应一个返回值
                                Object[] values = new Object[n];
                                for (int i = 0; i < n; i++) {
                                    values[i] = method.invoke(dataSrc, methodParamValues[i]);
                                }
                                if (values.length > 1) {
                                    // 需要将对象类型转换为真实的返回值类型
                                    field.set(newBean, convertReturnValues(values, field.getType()));
                                } else {
                                    field.set(newBean, values[0]);
                                }
                            }
                        });
                    }
                }
                tmpType = tmpType.getSuperclass();
            }
            return newBean;
        }, logFormat, dataSrc, beanType);
    }

    private static String getMethodName(final ValueFromMethod valueFromMethod, String fieldName) {
        // 默认方法名称是get+大驼峰字段名, 如果有不一致的, 通过ValueFromMethod注解指定
        return valueFromMethod == null ? "get" + HylfStringUtil.capFirstLetter(fieldName) : valueFromMethod.value();
    }

    private static Class<?>[] getMethodParamTypes(final ValueFromMethod valueFromMethod) {
        return valueFromMethod == null ? null : valueFromMethod.paramTypes();
    }

    private static Object[][] getMethodParamValues(final Class<?>[] methodParamTypes, final ValueFromMethod valueFromMethod) {
        int n = methodParamTypes.length;
        int groupNumber = valueFromMethod.groupNumber();
        Object[][] values = new Object[groupNumber][n];
        int[] indexArray = new int[9];
        for (int i = 0; i < groupNumber; i++) {
            for (int j = 0; j < n; j++) {
                for (Class<?> methodParamType : methodParamTypes) {
                    if (boolean.class.equals(methodParamType)) {
                        values[i][j] = valueFromMethod.booleanValues()[indexArray[0]++];
                    } else if (char.class.equals(methodParamType)) {
                        values[i][j] = valueFromMethod.charValues()[indexArray[1]++];
                    } else if (byte.class.equals(methodParamType)) {
                        values[i][j] = valueFromMethod.byteValues()[indexArray[2]++];
                    } else if (short.class.equals(methodParamType)) {
                        values[i][j] = valueFromMethod.shortValues()[indexArray[3]++];
                    } else if (int.class.equals(methodParamType)) {
                        values[i][j] = valueFromMethod.intValues()[indexArray[4]++];
                    } else if (long.class.equals(methodParamType)) {
                        values[i][j] = valueFromMethod.longValues()[indexArray[5]++];
                    } else if (double.class.equals(methodParamType)) {
                        values[i][j] = valueFromMethod.doubleValues()[indexArray[6]++];
                    } else if (float.class.equals(methodParamType)) {
                        values[i][j] = valueFromMethod.floatValues()[indexArray[7]++];
                    } else if (String.class.equals(methodParamType)) {
                        values[i][j] = valueFromMethod.stringValues()[indexArray[8]++];
                    } else {
                        throw new RuntimeException("入参类型不对。[methodParamType = " + methodParamType + "]");
                    }
                }
            }
        }
        return values;
    }

    private static Object convertReturnValues(final Object[] values, final Class<?> returnType) {
        if (char[].class.equals(returnType)) {
            return HylfCollectionUtil.objectArrayToCharArray(values, ' ');
        }
        if (boolean[].class.equals(returnType)) {
            return HylfCollectionUtil.objectArrayToBooleanArray(values, false);
        }
        if (byte[].class.equals(returnType)) {
            return HylfCollectionUtil.objectArrayToByteArray(values, (byte) 0);
        }
        if (short[].class.equals(returnType)) {
            return HylfCollectionUtil.objectArrayToShortArray(values, (short) 0);
        }
        if (int[].class.equals(returnType)) {
            return HylfCollectionUtil.objectArrayToIntArray(values, 0);
        }
        if (long[].class.equals(returnType)) {
            return HylfCollectionUtil.objectArrayToLongArray(values, 0L);
        }
        if (double[].class.equals(returnType)) {
            return HylfCollectionUtil.objectArrayToDoubleArray(values, 0.0);
        }
        if (float[].class.equals(returnType)) {
            return HylfCollectionUtil.objectArrayToFloatArray(values, 0.0F);
        }
        if (String[].class.equals(returnType)) {
            return HylfCollectionUtil.objectArrayToArray(values, "", String.class);
        }
        throw new RuntimeException("返回值类型无效。[returnType = " + returnType + "]");
    }

    /**
     * 将ResultSet的结果注入到指定类型的JavaBean中, 并返回一个List(可指定对象的字段怎么转换到数据库字段)
     *
     * @param rs                ResultSet
     * @param eleType           JavaBean类型
     * @param tableFieldConvert 转换方法
     * @return List
     */
    public static <T> List<T> autoSetValue(final ResultSet rs, final Class<T> eleType, OneReturnFunction<String, String> tableFieldConvert) {
        String logFormat = "自动赋值失败。[rs = {}, eleType = {}]";
        return HylfFunUtil.methodVoidReturnExec(() -> {
            List<T> values = new ArrayList<>();
            if (rs == null || eleType == null) {
                return values;
            }
            List<Field> fieldList = new ArrayList<>();
            Class<?> tmpType = eleType;
            while (tmpType != null) {
                Field[] fields = tmpType.getDeclaredFields();
                for (Field field : fields) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        continue;
                    }
                    // 该字段是否忽略, 即不注入值
                    InjectIgnore ignore = field.getAnnotation(InjectIgnore.class);
                    if (ignore != null) {
                        continue;
                    }
                    field.setAccessible(true);
                    fieldList.add(field);
                }
                tmpType = tmpType.getSuperclass();
            }
            while (rs.next()) {
                T newEle = eleType.getConstructor().newInstance();
                for (Field field : fieldList) {
                    setValue(field, newEle, rs, tableFieldConvert);
                }
                values.add(newEle);
            }
            return values;
        }, logFormat, rs, eleType);
    }

    /**
     * 将ResultSet的结果注入到指定类型的JavaBean中, 并返回一个List(默认字段名转换为全大写蛇形字符串和数据库字段对应)
     *
     * @param rs      ResultSet
     * @param eleType JavaBean类型
     * @return List
     */
    public static <T> List<T> autoSetValue(final ResultSet rs, final Class<T> eleType) {
        return autoSetValue(rs, eleType, s -> HylfStringUtil.humpToSnakeStr(s).toUpperCase(Locale.ROOT));
    }

    private static void setValue(final Field field, final Object obj, final ResultSet rs, OneReturnFunction<String, String> tableFieldConvert) {
        HylfFunUtil.methodVoidExec(() -> {
            Class<?> type = field.getType();
            String name = tableFieldConvert.apply(field.getName());
            if (boolean.class.equals(type)) {
                field.setBoolean(obj, rs.getBoolean(name));
            } else if (byte.class.equals(type)) {
                field.setByte(obj, rs.getByte(name));
            } else if (short.class.equals(type)) {
                field.setShort(obj, rs.getShort(name));
            } else if (int.class.equals(type)) {
                field.setInt(obj, rs.getInt(name));
            } else if (long.class.equals(type)) {
                field.setLong(obj, rs.getLong(name));
            } else if (double.class.equals(type)) {
                field.setDouble(obj, rs.getDouble(name));
            } else if (float.class.equals(type)) {
                field.setFloat(obj, rs.getFloat(name));
            } else {
                field.set(obj, rs.getObject(name));
            }
        });
    }


    /**
     * 判断指定对象有没有字段是null。
     *
     * @param object       object
     * @param ignoreFields 哪些字段不用判断。
     * @return true 表示有字段是null, false表示除忽略的字段外所有字段都不为空
     */
    public static boolean checkObjectFieldsHasNull(Object object, String... ignoreFields) {
        if (object == null) {
            return true;
        }
        List<String> ignoreList = List.of(ignoreFields);
        for (Field field : object.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            if (ignoreList.contains(field.getName())) {
                continue;
            }
            if (HylfFunUtil.methodOneReturnExec(field::get, object) == null) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断指定对象指定字段中有没有null的。
     *
     * @param object object
     * @param fields 需要判断哪些字段
     * @return true 表示有字段是null, false表示所有字段都不为空
     */
    public static boolean checkObjectFieldsIsNull(Object object, String... fields) {
        if (object == null) {
            return true;
        }
        List<String> ignoreList = List.of(fields);
        for (Field field : object.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            if (!ignoreList.contains(field.getName())) {
                continue;
            }
            if (HylfFunUtil.methodOneReturnExec(field::get, object) == null) {
                return true;
            }
        }
        return false;
    }
}
